import { Nodes } from '@knno/dom';
import { h, JSXComponent, JSXProps } from '@knno/dom/jsx';
import { DATA } from '../components/symbol';
import { foldableStyle } from './foldable/foldable.css';

type FoldDetails = {
	expanded: boolean;
};

export interface FoldableProp extends JSXProps<Foldable> {
	opened?: boolean;
	aniDuration?: number;
	onfoldchanged?: (evt: CustomEvent<FoldDetails>) => void;
}

export class Foldable extends JSXComponent<Foldable> {
	[DATA]: {
		aniDuration: number;
		opened: boolean;
		timer?: NodeJS.Timeout;
	};
	constructor(attrs: FoldableProp, children: Nodes[]) {
		super(<div class={foldableStyle}>{...children}</div>, attrs);
		this[DATA] = {
			opened: attrs.opened ?? true,
			aniDuration: attrs.aniDuration ?? 500,
			timer: undefined,
		};
		this.css({
			transition: `height ${this[DATA].aniDuration}ms`,
			height: this[DATA].opened ? '' : '0px',
		});
		if (this[DATA].opened) {
			this.removeClass('shrinked');
		} else {
			this.addClass('shrinked');
		}
	}

	private expand() {
		clearTimeout(this[DATA].timer);
		const currentHeight = this.height();
		this.css('height', '');
		this.removeClass('shrinked');
		const realHeight = this.height();
		this.css('height', currentHeight + 'px');
		setTimeout(() => {
			clearTimeout(this[DATA].timer);
			this.css({ height: realHeight + 'px' });
			this[DATA].timer = setTimeout(() => {
				this.css('height', '');
				this.emit<FoldDetails>('foldchanged', { expanded: true });
			}, this[DATA].aniDuration);
		}, 32);
	}

	private shrink() {
		clearTimeout(this[DATA].timer);
		const currentHeight = this.height();
		this.css({ height: currentHeight + 'px' });
		setTimeout(() => {
			this.css('height', '0px');
			this[DATA].timer = setTimeout(() => {
				this.addClass('shrinked');
				this.emit<FoldDetails>('foldchanged', { expanded: false });
			}, this[DATA].aniDuration);
		}, 32);
	}

	opened(): boolean;
	opened(value: boolean): this;
	opened(value?: boolean): boolean | this {
		if (value === undefined) {
			return this[DATA].opened;
		}
		this[DATA].opened = value;
		if (value) {
			this.expand();
		} else {
			this.shrink();
		}
		return this;
	}

	aniDuration(): number;
	aniDuration(value: number): this;
	aniDuration(value?: number): number | this {
		if (value === undefined) {
			return this[DATA].aniDuration;
		}
		this[DATA].aniDuration = value;
		return this;
	}

	toggle() {
		this.opened(!this.opened());
	}
}
